package com.jackson0714.passjava.gateway.config;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.jackson0714.common.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * @author zhangpeng148
 * @Description:动态路由配置  由于gateWay并没有适配nacos，需要自定义监听器
 * @Version v1.0.0
 * @date 2022/1/27 16:41
 */
@Component
@Slf4j
public class NacosDynamicRouteConfig implements ApplicationEventPublisherAware {

    @Value("${spring.application.name}" + "-" + "${spring.profiles.active}" + "." + "${spring.cloud.nacos.config.file-extension}")
    private String dataId;

    @Value("${spring.cloud.nacos.config.group}")
    private String group;

    @Value("${spring.cloud.nacos.config.namespace}")
    private String namespace;

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serveAddress;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> route_list = new ArrayList<>();


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @PostConstruct
    public void dynamicRouteListen() {
        try {
            //根据nacos注册中心地址获取服务
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, serveAddress);
            properties.put(PropertyKeyConst.NAMESPACE, namespace);
            ConfigService configService = NacosFactory.createConfigService(properties);
            //获取远程配置
            String config = configService.getConfig(dataId, group, 5000);
            log.info("根据dataId:{},group:{} 获取远程配置为：{}", dataId, group, config);
            configService.addListener(dataId, group, new Listener() {

                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    clearRoute();
                    if (StringUtils.isEmpty(configInfo)) {
                        return;
                    }
                    //转换     注意这里一般在nacos 单独使用json配置路由数据
                    List<RouteDefinition> routeDefinitionList = JsonUtils.nonNullMapper().fromJson(configInfo, JsonUtils.nonEmptyMapper().contructCollectionType(List.class, RouteDefinition.class));
                    for (RouteDefinition routeDefinition : routeDefinitionList) {
                        addRoute(routeDefinition);
                    }
                    publish();
                }
            });
        } catch (NacosException e) {
            log.error("dynamicRouteByNacosListener error" + e);
        }
    }

    /** 
     *推送事件
     *
     * @param: [] 
     * @return: void 
     */
    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

    /**
     * 新增路由
     *
     * @param: [routeDefinition]
     * @return: void
     */
    private void addRoute(RouteDefinition routeDefinition) {
        try {
            this.routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            route_list.add(routeDefinition.getId());
        } catch (Exception e) {
            log.error("新增路由异常:", e);
        }
    }

    /**
     * 清理路由
     *
     * @param: []
     * @return: void
     */
    private void clearRoute() {
        for (String id : route_list) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        route_list.clear();
    }
}
